package org.x.note.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * ZIP压缩工具
 * 
 */
public class ZipUtil {

	/**
	 * 后缀名
	 */
	public final static String SUFFIXN = ".zip";
	private static final String BASE_DIR = "";

	private static final int BUFFER = 1024;

	/**
	 * 压缩，压缩文件放在源文件目录
	 * 
	 * @param srcFile
	 *            源文件或目录
	 * @throws Exception
	 */
	public static void compress(File srcFile) throws IOException {
		if (!srcFile.exists()) {
			throw new FileNotFoundException(srcFile.getName() + "不存在");
		}

		StringBuffer destPath = new StringBuffer(srcFile.getParent());
		destPath.append(File.separator);
		destPath.append(srcFile.getName());
		destPath.append(SUFFIXN);

		compress(srcFile, destPath.toString());
	}

	/**
	 * 压缩
	 * 
	 * @param srcFile
	 *            源文件或目录
	 * @param destFile
	 *            压缩zip文件
	 * @throws IOException
	 */
	public static void compress(File srcFile, File destFile) throws IOException {
		CheckedOutputStream cos = null;
		ZipOutputStream zos = null;
		try {
			// 对输出文件做CRC32校验
			cos = new CheckedOutputStream(new FileOutputStream(destFile),
					new CRC32());

			zos = new ZipOutputStream(cos);

			compress(srcFile, zos, BASE_DIR);
		} finally {
			if (zos != null) {
				zos.flush();
				zos.close();
			}
			if (cos != null) {
				cos.close();
			}
		}
	}

	/**
	 * 压缩文件
	 * 
	 * @param srcFile
	 *            源文件
	 * @param destPath
	 *            压缩zip文件存储路径
	 * @throws IOException
	 */
	public static void compress(File srcFile, String destPath)
			throws IOException {
		compress(srcFile, new File(destPath));
	}

	/**
	 * 压缩
	 * 
	 * @param srcFile
	 *            源路径
	 * @param zos
	 *            ZipOutputStream
	 * @param basePath
	 *            压缩包内相对路径
	 * @throws Exception
	 */
	private static void compress(File srcFile, ZipOutputStream zos,
			String basePath) throws IOException {
		if (srcFile.isDirectory()) {
			compressDir(srcFile, zos, basePath);
		} else {
			compressFile(srcFile, zos, basePath);
		}
	}

	/**
	 * 压缩
	 * 
	 * @param srcPath
	 * @throws Exception
	 */
	public static void compress(String srcPath) throws IOException {
		File srcFile = new File(srcPath);

		compress(srcFile);
	}

	/**
	 * 文件压缩
	 * 
	 * @param srcPath
	 *            源文件路径
	 * @param destPath
	 *            目标文件路径
	 * 
	 */
	public static void compress(String srcPath, String destPath)
			throws IOException {
		File srcFile = new File(srcPath);

		compress(srcFile, destPath);
	}

	/**
	 * 压缩目录
	 * 
	 * @param dir
	 * @param zos
	 * @param basePath
	 * @throws Exception
	 */
	private static void compressDir(File dir, ZipOutputStream zos,
			String basePath) throws IOException {
		File[] files = dir.listFiles();

		// 构建空目录
		if (files.length < 1) {
			ZipEntry entry = new ZipEntry(basePath + dir.getName()
					+ File.separator);

			zos.putNextEntry(entry);
			zos.closeEntry();
		}

		for (File file : files) {
			// 递归压缩
			compress(file, zos, basePath + dir.getName() + File.separator);
		}
	}

	/**
	 * 文件压缩
	 * 
	 * @param file
	 *            待压缩文件
	 * @param zos
	 *            ZipOutputStream
	 * @param dir
	 *            压缩文件中的当前路径
	 * @throws Exception
	 */
	private static void compressFile(File file, ZipOutputStream zos, String dir)
			throws IOException {

		/**
		 * 压缩包内文件名定义
		 * 
		 * <pre>
		 * 如果有多级目录，那么这里就需要给出包含目录的文件名 
		 * 如果用WinRAR打开压缩包，中文名将显示为乱码
		 * </pre>
		 */
		ZipEntry entry = new ZipEntry(dir + file.getName());

		zos.putNextEntry(entry);

		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
				file));

		int count;
		byte data[] = new byte[BUFFER];
		while ((count = bis.read(data, 0, BUFFER)) != -1) {
			zos.write(data, 0, count);
		}
		bis.close();

		zos.closeEntry();
	}

	/**
	 * 文件 解压缩
	 * 
	 * @param srcPath
	 *            源文件路径
	 * @throws IOException
	 */
	public static void unCompress(String srcPath) throws IOException {
		File srcFile = new File(srcPath);
		if (!srcFile.exists()) {
			throw new FileNotFoundException(srcPath + "不存在");
		}

		unCompress(srcFile);
	}

	/**
	 * 解压缩
	 * 
	 * @param srcFile
	 * @throws Exception
	 */
	public static void unCompress(File srcFile) throws IOException {
		unCompress(srcFile, srcFile.getParent());
	}

	/**
	 * 解压缩
	 * 
	 * @param srcFile
	 * @param destFile
	 * @throws Exception
	 */
	public static void unCompress(File srcFile, File destFile)
			throws IOException {
		CheckedInputStream cis = null;
		ZipInputStream zis = null;
		try {
			cis = new CheckedInputStream(new FileInputStream(srcFile),
					new CRC32());

			zis = new ZipInputStream(cis);

			unCompress(destFile, zis);
		} finally {
			if (zis != null) {
				zis.close();
			}
			if (cis != null) {
				cis.close();
			}
		}

	}

	/**
	 * 解压缩
	 * 
	 * @param srcFile
	 * @param destPath
	 * @throws Exception
	 */
	public static void unCompress(File srcFile, String destPath)
			throws IOException {
		unCompress(srcFile, new File(destPath));

	}

	/**
	 * 文件 解压缩
	 * 
	 * @param srcPath
	 *            源文件路径
	 * @param destPath
	 *            目标文件路径
	 * @throws Exception
	 */
	public static void unCompress(String srcPath, String destPath)
			throws IOException {

		File srcFile = new File(srcPath);
		unCompress(srcFile, destPath);
	}

	/**
	 * 文件 解压缩
	 * 
	 * @param destFile
	 *            目标文件
	 * @param zis
	 *            ZipInputStream
	 * @throws Exception
	 */
	private static void unCompress(File destFile, ZipInputStream zis)
			throws IOException {

		ZipEntry entry = null;
		while ((entry = zis.getNextEntry()) != null) {
			// 文件
			String dir = destFile.getPath() + File.separator + entry.getName();
			File dirFile = new File(dir);

			// 文件检查
			fileProber(dirFile);

			if (entry.isDirectory()) {
				dirFile.mkdirs();
			} else {
				unCompressFile(dirFile, zis);
			}
			zis.closeEntry();
		}
	}

	/**
	 * 文件探针
	 * 
	 * 
	 * 当父目录不存在时，创建目录！
	 * 
	 * 
	 * @param dirFile
	 */
	private static void fileProber(File dirFile) {

		File parentFile = dirFile.getParentFile();
		if (!parentFile.exists()) {
			// 递归寻找上级目录
			fileProber(parentFile);

			parentFile.mkdir();
		}
	}

	/**
	 * 文件解压缩
	 * 
	 * @param destFile
	 *            目标文件
	 * @param zis
	 *            ZipInputStream
	 * @throws IOException
	 * @throws Exception
	 */
	private static void unCompressFile(File destFile, ZipInputStream zis)
			throws IOException {

		BufferedOutputStream bos = null;
		try {
			bos = new BufferedOutputStream(new FileOutputStream(destFile));
			int count;
			byte data[] = new byte[BUFFER];
			while ((count = zis.read(data, 0, BUFFER)) != -1) {
				bos.write(data, 0, count);
			}

		} finally {
			if (bos != null) {
				bos.close();
			}
		}
	}
}
